/*
 * Copyright 2011 Niklas Kyster Rasmussen
 * 
 * This file is part of WotXP.
 *
 * WotXP is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * WotXP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WotXP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

package net.nikr.wot.gui;

import net.nikr.wot.gui.shared.PaddingTableCellRenderer;
import net.nikr.wot.gui.data.Left;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.swing.EventTableModel;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Ellipse2D;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import net.nikr.wot.calc.Calc;
import net.nikr.wot.calc.Traning;
import net.nikr.wot.gui.data.Next;
import net.nikr.wot.gui.data.Next.ShowNext;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;


public class MainPanel implements FocusListener, KeyListener{
	
	private final static int FIELD_LENGHT = 75;
	
	private JPanel jPanel;
	private  GroupLayout layout;
	
	private JTextField jAvg;
	private JTextField jCrew;
	private JTextField jPrimary;
	private JTextField jSecundary1;
	private JTextField jSecundary2;
	private JTextField jSecundary3;
	
	private JComboBox jLevels;
	
	private JTabbedPane tabbedPane;
	private JFreeChart jNextChart;
	private JTable jNextTable;
	
	private JTable jLeftTable;
	
	private JButton jClearAll;
	private JButton jClearSkills;
	
	private XYSeriesCollection dataset = new XYSeriesCollection();
	private EventList<Left> leftEventList = new BasicEventList<Left>();
	private EventList<Next> nextEventList = new BasicEventList<Next>();
	private List<Component> components = new ArrayList<Component>();
	
	private MainFrame mainFrame;

	public MainPanel(MainFrame mainFrame) {
		this.mainFrame = mainFrame;
		jPanel = new JPanel();
		layout = new GroupLayout(jPanel);
		jPanel.setLayout(layout);
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);
	
	//Input
		JLabel jInputLabel = createInfoLable("Input");
		
		jAvg = new JTextField();
		jAvg.addFocusListener(this);
		jAvg.addKeyListener(this);
		components.add(jAvg);
		JLabel jAvgLabel = new JLabel("Average xp per battle");
		
		
		jCrew = new JTextField();
		jCrew.addFocusListener(this);
		jCrew.addKeyListener(this);
		components.add(jCrew);
		JLabel jCrewLabel = new JLabel("Crew in tank");
		
		jPrimary = new JTextField();
		jPrimary.addFocusListener(this);
		jPrimary.addKeyListener(this);
		components.add(jPrimary);
		JLabel jPrimaryLabel = new JLabel("Primary");
		
		jSecundary1 = new JTextField();
		jSecundary1.addFocusListener(this);
		jSecundary1.addKeyListener(this);
		components.add(jSecundary1);
		JLabel jSecundary1Label = new JLabel("Secundary 1");
		
		jSecundary2 = new JTextField();
		jSecundary2.addFocusListener(this);
		jSecundary2.addKeyListener(this);
		components.add(jSecundary2);
		JLabel jSecundary2Label = new JLabel("Secundary 2");
		
		jSecundary3 = new JTextField();
		jSecundary3.addFocusListener(this);
		jSecundary3.addKeyListener(this);
		components.add(jSecundary3);
		JLabel jSecundary3Label = new JLabel("Secundary 3");
		
		jClearSkills = new JButton("Clear Skills");
		jClearSkills.addKeyListener(this);
		components.add(jClearSkills);
		jClearSkills.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				clearSkills();
				jPrimary.requestFocusInWindow();
			}
		});		
		
		jClearAll = new JButton("Clear All");
		jClearAll.addKeyListener(this);
		components.add(jClearAll);
		jClearAll.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				clearAll();
				jAvg.requestFocusInWindow();
			}
		});
		
		JLabel jSettingsLabel = createInfoLable("Settings");
		
		jLevels = new JComboBox(ShowNext.values());
		jLevels.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				validate();
			}
		});
		jLevels.addFocusListener(this);
		JLabel jLevelsLabel = new JLabel("Show next X levels:");
		
		JLabel jHelpLabel = createInfoLable("Help");
		
		JTextArea jHelp = new JTextArea("Use [Enter] and [Tab] to navigate");
		jHelp.setFont(jPanel.getFont());
		jHelp.setOpaque(false);
		jHelp.setBackground(jPanel.getBackground());
		jHelp.setEditable(false);
		jHelp.setLineWrap(true);
		jHelp.setWrapStyleWord(true);
		jHelp.setFocusable(false);
		
	//To 100%
		EventTableModel<Next> nextModel = new EventTableModel<Next>(nextEventList, new NextTableFormat());
		jNextTable = new JTable(nextModel);
		jNextTable.setDefaultRenderer(Number.class, new NumberCellRenderer());
		PaddingTableCellRenderer.install(jNextTable, 3);
		JScrollPane jNextTableScroll = new JScrollPane(jNextTable);
		
		jNextChart = ChartFactory.createXYLineChart(
			Traning.PRIMARY.getName(), // Title
			"Level", // x-axis Label
			"Total XP", // y-axis Label
			dataset, // Dataset
			PlotOrientation.VERTICAL, // Plot Orientation
			false, // Show Legend
			true, // Use tooltips
			false // Configure chart to generate URLs?
			);
		jNextChart.setAntiAlias(true);
		jNextChart.setBackgroundPaint(jPanel.getBackground());
		jNextChart.getXYPlot().setBackgroundPaint(Color.WHITE);
		jNextChart.getXYPlot().setRangeGridlinePaint(Color.LIGHT_GRAY);
		jNextChart.getXYPlot().setDomainGridlinePaint(Color.LIGHT_GRAY);
		
		jNextChart.getXYPlot().getRangeAxis().setStandardTickUnits(NumberAxis.createIntegerTickUnits());
		jNextChart.getXYPlot().getDomainAxis().setStandardTickUnits(NumberAxis.createIntegerTickUnits());
		final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, true);
		renderer.setSeriesStroke(0, new BasicStroke(1));
		renderer.setSeriesShape(0, new Ellipse2D.Float(-2.0f, -2.0f, 4.0f, 4.0f));
        jNextChart.getXYPlot().setRenderer(renderer);
		
		ChartPanel jChartPanel = new ChartPanel(jNextChart);
		
		tabbedPane = new JTabbedPane();
		tabbedPane.add("Table", jNextTableScroll);
		tabbedPane.add("Chart", jChartPanel);
		
		EventTableModel<Left> leftModel = new EventTableModel<Left>(leftEventList, new LeftTableFormat());
		jLeftTable = new JTable(leftModel);
		jLeftTable.setDefaultRenderer(Number.class, new NumberCellRenderer());
		PaddingTableCellRenderer.install(jLeftTable, 3);
		JScrollPane jNeededTableScroll = new JScrollPane(jLeftTable);
		
		layout.setHorizontalGroup(
			layout.createSequentialGroup()
				.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
					.addGroup(layout.createSequentialGroup()
						.addGap(10)
						.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
							.addComponent(jAvgLabel)
							.addComponent(jCrewLabel)
							.addComponent(jPrimaryLabel)
							.addComponent(jSecundary1Label)
							.addComponent(jSecundary2Label)
							.addComponent(jSecundary3Label)
							.addComponent(jLevelsLabel)
						)
						.addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING)
							.addComponent(jAvg, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							.addComponent(jCrew, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							.addComponent(jPrimary, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							.addComponent(jSecundary1, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							.addComponent(jSecundary2, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							.addComponent(jSecundary3, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							.addComponent(jLevels, FIELD_LENGHT, FIELD_LENGHT, FIELD_LENGHT)
							
						)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
						.addGroup(layout.createSequentialGroup()
							.addComponent(jClearSkills, 90, 90, 90)
							.addComponent(jClearAll, 90, 90, 90)
						)
						.addComponent(jInputLabel, 210, 210, 210)
						.addComponent(jSettingsLabel, 210, 210, 210)
						.addComponent(jHelpLabel, 210, 210, 210)
					)
					.addGroup(layout.createSequentialGroup()
						.addGap(10)
						.addComponent(jHelp, 170, 170 ,170)
					)
				)
				.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
					.addComponent(tabbedPane)
					.addComponent(jNeededTableScroll)
				)
		);
		layout.setVerticalGroup(
			layout.createParallelGroup()
				.addGroup(layout.createSequentialGroup()
					.addComponent(jInputLabel)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jAvgLabel, 22, 22, 22)
						.addComponent(jAvg, 22, 22, 22)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jCrewLabel, 22, 22, 22)
						.addComponent(jCrew, 22, 22, 22)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jPrimaryLabel, 22, 22, 22)
						.addComponent(jPrimary, 22, 22, 22)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jSecundary1Label, 22, 22, 22)
						.addComponent(jSecundary1, 22, 22, 22)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jSecundary2Label, 22, 22, 22)
						.addComponent(jSecundary2, 22, 22, 22)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jSecundary3Label, 22, 22, 22)
						.addComponent(jSecundary3, 22, 22, 22)
					)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jClearSkills)
						.addComponent(jClearAll)
					)
					.addGap(20)
					.addComponent(jSettingsLabel)
					.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
						.addComponent(jLevelsLabel, 22, 22, 22)
						.addComponent(jLevels, 22, 22, 22)
					)
					.addGap(20)
					.addComponent(jHelpLabel)
					.addComponent(jHelp)
				)
				.addGroup(layout.createSequentialGroup()
					.addComponent(tabbedPane)
					.addComponent(jNeededTableScroll, 100, 100, 100)
				)
				.addGap(5)
		);
		validate();
	}

	public JPanel getPanel() {
		return jPanel;
	}
	
	public void show(){
		jAvg.requestFocusInWindow();
	}
	
	private void clearSkills(){
		jPrimary.setText("50");
		jSecundary1.setText("0");
		jSecundary2.setText("0");
		jSecundary3.setText("0");
	}
	
	private void clearAll(){
		jAvg.setText("0");
		jCrew.setText("0");
		jPrimary.setText("50");
		jSecundary1.setText("0");
		jSecundary2.setText("0");
		jSecundary3.setText("0");
	}
	
	private void validate(){
		int avg = get(jAvg);
		int crew = get(jCrew);
		int primary = get(jPrimary);
		int secondary1 = get(jSecundary1);
		int secondary2 = get(jSecundary2);
		int secondary3 = get(jSecundary3);
		Traning traning;
		int from;
		if (secondary3 > 0 || secondary2 >= 100){
			traning = Traning.SECONDARY3;
			from = secondary3;
			jPrimary.setText("100");
			primary = 100;
			jSecundary1.setText("100");
			secondary1 = 100;
			jSecundary2.setText("100");
			secondary2 = 100;
		} else if (secondary2 > 0 || secondary1 >= 100){
			traning = Traning.SECONDARY2;
			from = secondary2;
			jPrimary.setText("100");
			primary = 100;
			jSecundary1.setText("100");
			secondary1 = 100;
		} else if (secondary1 > 0 || primary >= 100){
			from = secondary1;
			traning = Traning.SECONDARY1;
			jPrimary.setText("100");
			primary = 100;
		} else {
			if (primary < 50){
				primary = 50;
				jPrimary.setText("50");
			}
			from = primary;
			traning = Traning.PRIMARY;
		}
		int[] xpLeft = new int[4];
		xpLeft[0] = Calc.xpLeft(primary, Traning.PRIMARY);
		xpLeft[1] = Calc.xpLeft(primary, Traning.PRIMARY)
					+Calc.xpLeft(secondary1, Traning.SECONDARY1);
		xpLeft[2] = Calc.xpLeft(primary, Traning.PRIMARY)
					+Calc.xpLeft(secondary1, Traning.SECONDARY1)
					+Calc.xpLeft(secondary2, Traning.SECONDARY2);
		xpLeft[3] = Calc.xpLeft(primary, Traning.PRIMARY)
					+Calc.xpLeft(secondary1, Traning.SECONDARY1)
					+Calc.xpLeft(secondary2, Traning.SECONDARY2)
					+Calc.xpLeft(secondary3, Traning.SECONDARY3);
		
		ShowNext showNext = (ShowNext) jLevels.getSelectedItem(); //.getItemAt(jLevels.getSelectedIndex());
		updateNextTable(from, showNext.getValue(), traning);
		updateNextChart(from, showNext.getValue(), traning);
		updateLeftTable(xpLeft, avg, crew);
	}
	
	private void updateNextTable(int from, int levels, Traning traning){
		int loops = Math.min(levels, 100-from);
		int total = 0;
		nextEventList.getReadWriteLock().writeLock().lock();
		try {
			nextEventList.clear();
			for (int i = 0, a = from; i < loops; i++, a++){
				int xp = Calc.xpToNextLevel(a, traning);
				total += xp;
				nextEventList.add( new Next(String.valueOf(a+1), xp, total));
			}
		} finally {
			nextEventList.getReadWriteLock().writeLock().unlock();
		}
	}
	
	private int updateNextChart(int from, int levels, Traning traning){
		int loops = Math.min(levels, 100-from);
		int total = 0;
		//int total = Calc.xpDone(from, traning);
		dataset.removeAllSeries();
		jNextChart.setTitle(traning.getName());
		XYSeries totalSeries = new XYSeries(traning.getName()+" Total");
		totalSeries.add(from, total); //Current level
		for (int i = 0, a = from; i < loops; i++, a++){
			//int xp = Calc.xpToNextLevel(a, traning);
			total += Calc.xpToNextLevel(a, traning);
			totalSeries.add(a+1, total);
		}
		dataset.addSeries(totalSeries);
		
		return total;
	}

	private void updateLeftTable(int[] xpLeft, int avg, int crew){
		leftEventList.getReadWriteLock().writeLock().lock();
		try {
			leftEventList.clear();
			leftEventList.add( new Left("XP", xpLeft[0], xpLeft[1], xpLeft[2], xpLeft[3]));
			if (avg != 0){
				leftEventList.add( new Left("Battles", Calc.battlesLeft(xpLeft[0], avg), Calc.battlesLeft(xpLeft[1], avg), Calc.battlesLeft(xpLeft[2], avg), Calc.battlesLeft(xpLeft[3], avg)));
				if (crew != 0){
					leftEventList.add( new Left("Battles (Rapid)", Calc.battlesLeftRapid(xpLeft[0], avg, crew), Calc.battlesLeftRapid(xpLeft[1], avg, crew), Calc.battlesLeftRapid(xpLeft[2], avg, crew), Calc.battlesLeftRapid(xpLeft[3], avg, crew)));
				}
			}
		} finally {
			leftEventList.getReadWriteLock().writeLock().unlock();
		}
	}
	
	private int get(Component component){
		if (component instanceof JTextField){
			return get((JTextField) component);
		} else {
			return 0;
		}
	}
	
	private int get(JTextField jTextField){
		String s = jTextField.getText();
		try {
			return Integer.valueOf(s);
		} catch (NumberFormatException ex){
			jTextField.setText("0");
			return 0;
		}
	}
	
	private void set(Component component, int text){
		if (component instanceof JTextField){
			set((JTextField) component, Integer.toString(text));
		}
	}
	
	private void set(JTextField jTextField, String text){
		jTextField.setText(text);
	}

	@Override
	public void focusGained(FocusEvent e) {
		validate();
		Object o = e.getSource();
		if (o instanceof JTextField){
			JTextField jTextField = (JTextField) o;
			jTextField.selectAll();
		}
	}

	@Override
	public void focusLost(FocusEvent e) {
		validate();
	}
	
	private JLabel createInfoLable(String text){
		JLabel jLabel = new JLabel(text);
		jLabel.setFont( new Font(jLabel.getFont().getFamily(), Font.BOLD, jLabel.getFont().getSize()));
		jLabel.setBorder(BorderFactory.createCompoundBorder(
								BorderFactory.createLineBorder(Color.BLACK, 1)
								,BorderFactory.createEmptyBorder(5, 5, 5, 5)
				));
		jLabel.setBackground(jLabel.getBackground().darker());
		jLabel.setOpaque(true);
		return jLabel;
	}

	@Override
	public void keyTyped(KeyEvent e) {
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		Component c = e.getComponent();
		if (e.getKeyCode() == KeyEvent.VK_ENTER){
			if (components.contains(c)){
				if (c != null && (c.equals(jPrimary) || c.equals(jSecundary1) || c.equals(jSecundary2) || c.equals(jSecundary3))){
					int text = get(c);
					if (text != 0){
						clearSkills();
						set(c, text);
					}
				}
				int index = components.indexOf(c) + 1;
				if (index >= components.size()) index = 0;
				Component nextComponent = components.get(index);
				if (c.equals(jClearSkills)){
					jPrimary.requestFocusInWindow();
				} else {
					nextComponent.requestFocusInWindow();
				}
			}
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		
	}
	
	public static class NumberCellRenderer extends DefaultTableCellRenderer {
		
		private static final long serialVersionUID = 1L;
		
		private static DecimalFormat longFormat  = new DecimalFormat("#,##0");
		
		public NumberCellRenderer() {
			this.setHorizontalTextPosition(SwingConstants.RIGHT);
			this.setHorizontalAlignment(SwingConstants.RIGHT);
		}
		@Override
		public void setValue(Object value) {
			setText((value == null) ? "" : longFormat.format(value));
		}
	}
}
